# debug.rb
# Copyright (C) 2006-2009 Akira TAGOH

# Authors:
#   Akira TAGOH  <akira@tagoh.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

require 'singleton'


module PRUNE

=begin rdoc

== PRUNE::DebugCategory

=end

  class DebugCategory
    include Singleton

=begin rdoc

=== PRUNE::DebugCategory#new

=end

    def initialize
      @categories = []
    end # def initialize

=begin rdoc

=== PRUNE::DebugCategory#add(*categories)

=end

    def add(*categories)
      categories.each do |c|
        @categories.push(c)
      end
    end # def add

=begin rdoc

=== PRUNE::DebugCategory#remove(*categories)

=end

    def remove(*categories)
      categories.each do |c|
        @categories.delete(c)
      end
    end # def remove

=begin rdoc

=== PRUNE::DebugCategory#list

=end

    def list
      return @categories
    end # def list

  end # class DebugCategory

=begin rdoc

== PRUNE::DebugCategoryList

=end

  class DebugCategoryList < PRUNE::DebugCategory
  end # class DebugCategoryList

=begin rdoc

== PRUNE::Debug

=end

  module Debug
    PRIOR_FIXME = 1
    PRIOR_BUG = 2
    PRIOR_WARNING = 3
    PRIOR_INFO = 4
    PRIOR_DEBUG = 5

    @@PriorityMaps = {
      PRIOR_FIXME=>'FIXME!!!',
      PRIOR_BUG=>'[BUG]',
      PRIOR_WARNING=>'WARNING ***:',
      PRIOR_INFO=>'INFO ***:',
      PRIOR_DEBUG=>'DEBUG ***:',
    }

    class << self
      include PRUNE::Debug
    end

=begin rdoc

=== PRUNE::Debug#categories

=end

    def categories
      return PRUNE::DebugCategory.instance
    end # def categories

=begin rdoc

=== PRUNE::Debug#category_list

=end

    def category_list
      return PRUNE::DebugCategoryList.instance
    end # def category_list

=begin rdoc

=== PRUNE::Debug#output(priority, format, *message)

=end

    def output(priority, format, *message)
      prefix = @@PriorityMaps[priority]
      handler_prefix = prefix.split(//).reject{|x| x !~ /[a-zA-Z0-9]+/}.join.downcase

      dmesg = _decode_message(*message)
      unless dmesg.kind_of?(Array) then
        dmesg = [dmesg]
      end
      fs = sprintf("%s %s", prefix, format)
      msg = sprintf(fs, *dmesg)
      flag = false
      if PRUNE.public_methods.include?("#{handler_prefix}_output") then
        eval("PRUNE.#{handler_prefix}_output(msg)")
        flag = true
      end
      if !flag || priority <= PRIOR_WARNING then
        if defined?(PRUNE._output) then
          PRUNE._output(msg)
        else
          Kernel.printf("%s\n", msg)
        end
      end
    end # def output

=begin rdoc

=== PRUNE::Debug#debug(category, format, *message)

=end

    def debug(category, format, *message)
      cat = self.categories
      cat.add(category) unless cat.list.include?(category)
      self.output(PRIOR_DEBUG, format, *message) if $DEBUG && self.category_list.list.include?(category)
    end # def debug

=begin rdoc

=== PRUNE::Debug#info(format, *message)

=end

    def info(format, *message)
      self.output(PRIOR_INFO, format, *message)
    end # def info

=begin rdoc

=== PRUNE::Debug#warning(format, *message)

=end

    def warning(format, *message)
      self.output(PRIOR_WARNING, format, *message)
    end # def warning

=begin rdoc

=== PRUNE::Debug#bug(format, *message)

=end

    def bug(format, *message)
      self.output(PRIOR_BUG, format, *message)
    end # def bug

=begin rdoc

=== PRUNE::Debug#FIXME(format, *message)

=end

    def FIXME(format, *message)
      self.output(PRIOR_FIXME, format, *message)
    end # def FIXME

=begin rdoc

=== PRUNE::Debug#debug_sprintf(format, *args)

=end

    def debug_sprintf(format, *args)
      dmesg = _decode_message(*args)
      unless dmesg.kind_of?(Array) then
        dmesg = [dmesg]
      end
      return sprintf(format, *dmesg)
    end # def debug_sprintf

    # :enddoc:

    private

    def _decode_message(*val)
      retval = []

      val.each do |value|
        if value.kind_of?(Array) then
          tmp = ''
          value.each do |v|
            tmp << ', ' unless tmp.empty?
            tmp << sprintf("%s", _decode_message(v))
          end
          retval.push(sprintf("[%s]", tmp))
        elsif value.kind_of?(Hash) then
          tmp = ''
          value.each do |k, v|
            tmp << ', ' unless tmp.empty?
            kk = _decode_message(k)
            vv = _decode_message(v)
            tmp << sprintf("%s=>%s", kk, vv)
          end
          retval.push(sprintf("{%s}", tmp))
        elsif value.kind_of?(String) then
          if value.nil? then
            retval.push('nil')
          else
            retval.push(sprintf("\"%s\"", value))
          end
        elsif value.kind_of?(Symbol) then
          retval.push(sprintf(":%s", value))
        elsif value.kind_of?(Integer) then
          retval.push(value)
        elsif value.kind_of?(NilClass) then
          retval.push('NIL')
        elsif value.kind_of?(TrueClass) then
          retval.push('TRUE')
        elsif value.kind_of?(FalseClass) then
          retval.push('FALSE')
        else
          retval.push(sprintf("%s:%s", value.class, value))
        end
      end # val.each

      if retval.length == 1 then
        retval = retval[0]
      end

      return retval
    end # def _decode_message

  end # module Debug

end # module PRUNE
